package mornd.test1;

import org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext;
import org.springframework.context.ApplicationContext;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
import org.springframework.web.servlet.HandlerExecutionChain;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Map;

/**
 * @author: mornd
 * @dateTime: 2023/5/7 - 13:26
 *
 */
public class App {
    /*
    Root WebApplicationContext: initialization completed in 1746 ms
    Tomcat started on port(s): 8080 (http) with context path ''

    所有参数解析器：27
            org.springframework.web.method.annotation.RequestParamMethodArgumentResolver@7859e786
            org.springframework.web.method.annotation.RequestParamMapMethodArgumentResolver@285d851a
            org.springframework.web.servlet.mvc.method.annotation.PathVariableMethodArgumentResolver@314b8f2d
            org.springframework.web.servlet.mvc.method.annotation.PathVariableMapMethodArgumentResolver@664a9613
            org.springframework.web.servlet.mvc.method.annotation.MatrixVariableMethodArgumentResolver@5118388b
            org.springframework.web.servlet.mvc.method.annotation.MatrixVariableMapMethodArgumentResolver@15a902e7
            org.springframework.web.servlet.mvc.method.annotation.ServletModelAttributeMethodProcessor@7876d598
            org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor@4a3e3e8b
            org.springframework.web.servlet.mvc.method.annotation.RequestPartMethodArgumentResolver@5af28b27
            org.springframework.web.method.annotation.RequestHeaderMethodArgumentResolver@71104a4
            org.springframework.web.method.annotation.RequestHeaderMapMethodArgumentResolver@4985cbcb
            org.springframework.web.servlet.mvc.method.annotation.ServletCookieValueMethodArgumentResolver@72f46e16
            org.springframework.web.method.annotation.ExpressionValueMethodArgumentResolver@3c9168dc
            org.springframework.web.servlet.mvc.method.annotation.SessionAttributeMethodArgumentResolver@332a7fce
            org.springframework.web.servlet.mvc.method.annotation.RequestAttributeMethodArgumentResolver@549621f3
            org.springframework.web.servlet.mvc.method.annotation.ServletRequestMethodArgumentResolver@54361a9
            org.springframework.web.servlet.mvc.method.annotation.ServletResponseMethodArgumentResolver@32232e55
            org.springframework.web.servlet.mvc.method.annotation.HttpEntityMethodProcessor@5217f3d0
            org.springframework.web.servlet.mvc.method.annotation.RedirectAttributesMethodArgumentResolver@37ebc9d8
            org.springframework.web.method.annotation.ModelMethodProcessor@293bb8a5
            org.springframework.web.method.annotation.MapMethodProcessor@2416a51
            org.springframework.web.method.annotation.ErrorsMethodArgumentResolver@6fa590ba
            org.springframework.web.method.annotation.SessionStatusMethodArgumentResolver@6e9319f
            org.springframework.web.servlet.mvc.method.annotation.UriComponentsBuilderMethodArgumentResolver@72e34f77
            org.springframework.web.servlet.mvc.method.annotation.PrincipalMethodArgumentResolver@389adf1d
            org.springframework.web.method.annotation.RequestParamMethodArgumentResolver@77307458
            org.springframework.web.servlet.mvc.method.annotation.ServletModelAttributeMethodProcessor@1fc0053e
            所有返回值解析器：15
            org.springframework.web.servlet.mvc.method.annotation.ModelAndViewMethodReturnValueHandler@290b1b2e
            org.springframework.web.method.annotation.ModelMethodProcessor@47874b25
            org.springframework.web.servlet.mvc.method.annotation.ViewMethodReturnValueHandler@33617539
            org.springframework.web.servlet.mvc.method.annotation.ResponseBodyEmitterReturnValueHandler@2c177f9e
            org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBodyReturnValueHandler@5db4c359
            org.springframework.web.servlet.mvc.method.annotation.HttpEntityMethodProcessor@209775a9
            org.springframework.web.servlet.mvc.method.annotation.HttpHeadersReturnValueHandler@18e7143f
            org.springframework.web.servlet.mvc.method.annotation.CallableMethodReturnValueHandler@f9b7332
            org.springframework.web.servlet.mvc.method.annotation.DeferredResultMethodReturnValueHandler@74cec793
            org.springframework.web.servlet.mvc.method.annotation.AsyncTaskMethodReturnValueHandler@6fefce9e
            org.springframework.web.servlet.mvc.method.annotation.ServletModelAttributeMethodProcessor@4f8969b0
            org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor@1bdf8190
            org.springframework.web.servlet.mvc.method.annotation.ViewNameMethodReturnValueHandler@192f2f27
            org.springframework.web.method.annotation.MapMethodProcessor@8a589a2
            org.springframework.web.servlet.mvc.method.annotation.ServletModelAttributeMethodProcessor@6b5176f2
     */
    public static void main(String[] args) throws Throwable {
        AnnotationConfigServletWebServerApplicationContext context
                = new AnnotationConfigServletWebServerApplicationContext(WebConfig.class);

        /*
            初始化时就生成路径与控制器方法之前的映射关系
         */
        RequestMappingHandlerMapping handlerMapping = context.getBean(RequestMappingHandlerMapping.class);
        // 得到请求路径与控制器方法的映射结果
        Map<RequestMappingInfo, HandlerMethod> handlerMethods = handlerMapping.getHandlerMethods();
        handlerMethods.forEach((k,v) -> {
            // {GET [/test2]}->mornd.test1.Controller1#test2()
            System.out.println(k + "->" + v);
        });


        // Mock 模拟request 请求
        MockHttpServletRequest mockRequest = new MockHttpServletRequest("POST", "/test2");
        mockRequest.setParameter("name", "张三");
        // 返回控制器方法和拦截器链
        HandlerExecutionChain chain = handlerMapping.getHandler(mockRequest);

        System.out.println(chain);
        System.out.println("------------------");

        RequestMappingHandlerAdapter handlerAdapter = context.getBean(RequestMappingHandlerAdapter.class);

        // 通过 handlerAdapter 真正执行 controller 中的方法，由于方法是受保护的，这里不得已通过反射调用
        Method invokeHandlerMethod = RequestMappingHandlerAdapter.class
                .getDeclaredMethod("invokeHandlerMethod",
                        HttpServletRequest.class, HttpServletResponse.class, HandlerMethod.class);
        invokeHandlerMethod.setAccessible(true);
        ModelAndView modelAndView = (ModelAndView) invokeHandlerMethod.invoke(
                handlerAdapter, mockRequest,
                new MockHttpServletResponse(), (HandlerMethod) chain.getHandler());

        // 获取参数解析器
        List<HandlerMethodArgumentResolver> argumentResolvers = handlerAdapter.getArgumentResolvers();
        System.out.println("所有参数解析器：" + argumentResolvers.size());
        for (HandlerMethodArgumentResolver name : argumentResolvers) {
            System.out.println(name);

        }

        List<HandlerMethodReturnValueHandler> returnValueHandlers = handlerAdapter.getReturnValueHandlers();
        System.out.println("所有返回值解析器：" + returnValueHandlers.size());
        for (HandlerMethodReturnValueHandler name : returnValueHandlers) {
            System.out.println(name);
        }

        System.out.println("=============================");
        customRequest(context);

        //System.out.println(modelAndView);

    }

    /**
     * 自定义请求
     * @param applicationContext
     * @throws Throwable
     */
    private static void customRequest(ApplicationContext applicationContext) throws Throwable {
        RequestMappingHandlerMapping handlerMapping = applicationContext.getBean(RequestMappingHandlerMapping.class);
        MockHttpServletRequest mockRequest =
                new MockHttpServletRequest("GET", "/test4");
        mockRequest.addHeader("token", "123456");

        MockHttpServletResponse response = new MockHttpServletResponse();

        RequestMappingHandlerAdapter handlerAdapter = applicationContext.getBean(RequestMappingHandlerAdapter.class);
        Method invokeHandlerMethod = RequestMappingHandlerAdapter.class.getDeclaredMethod("invokeHandlerMethod",
                    HttpServletRequest.class, HttpServletResponse.class, HandlerMethod.class);
        invokeHandlerMethod.setAccessible(true);
        ModelAndView modelAndView = (ModelAndView) invokeHandlerMethod.invoke(
                handlerAdapter, mockRequest,
                response, (HandlerMethod) handlerMapping.getHandler(mockRequest).getHandler());

        // 获取响应内容
        String contentAsString = response.getContentAsString();
        System.out.println(contentAsString);
        System.out.println(modelAndView);
    }
}
